package com.jt808.decode.utils;

import com.jt808.decode.constant.AuxiliaryConstant;
import com.jt808.decode.constant.SensorConstant;
import com.jt808.decode.model.Jt808Message;
import com.jt808.decode.model.PassThroughData;
import com.jt808.decode.model.SensorLogData;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Data uplink transparent transmission
 * @date 2023-03-03
 */
public class Message0900Parser {

    /**
     * Data uplink transparent transmission
     * @param msg
     * @param msgBodyBuf
     * @return
     */
    public static PassThroughData parser(Jt808Message msg, ByteBuf msgBodyBuf){
        //Transparent message type
        int msgType = msgBodyBuf.readUnsignedByte();
        //Transparent message content
        Object msgCount;
        if(msgType==0x54){
            SensorLogData sensorLogData=parse54(msg,msgBodyBuf);
            msgCount= sensorLogData;
        }else {
            //Transparent message content
            int msgContentLen = msgBodyBuf.readableBytes();
            byte[] msgContentArr = null;
            if (msgContentLen > 0) {
                msgContentArr = new byte[msgContentLen];
                msgBodyBuf.readBytes(msgContentArr);
            }
            msgCount=ByteBufUtil.hexDump(msgContentArr);
        }
        PassThroughData passThroughData=new PassThroughData();
        passThroughData.setDeviceId(msg.getDeviceId());
        passThroughData.setHexMsgId(NumberUtil.formatMessageId(msg.getMsgId()));
        passThroughData.setMsgFlowId(msg.getMsgFlowId());
        passThroughData.setMsgType(msgType);
        passThroughData.setMsgContent(msgCount);
        return passThroughData;
    }
    /**
     * Parsing transparent 54 packets
     * @param msg
     * @param msgBody
     * @return
     * @throws Exception
     */
    public static SensorLogData parse54(Jt808Message msg, ByteBuf msgBody) {
        //terminal status
        long status = msgBody.readUnsignedInt();
        //lat
        double lat = NumberUtil.multiply(msgBody.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //lon
        double lon = NumberUtil.multiply(msgBody.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //altitude,unit: m
        int altitude = msgBody.readUnsignedShort();
        //speed ,unit: km/h
        double speed = NumberUtil.multiply(msgBody.readUnsignedShort(), NumberUtil.ONE_PRECISION);
        //direction (0~360;0:north)
        int direction = msgBody.readShort();
        //gnss time
        byte[] timeArr = new byte[6];
        msgBody.readBytes(timeArr);
        String bcdTimeStr = ByteBufUtil.hexDump(timeArr);
        ZonedDateTime gpsZonedDateTime = CommonUtil.parseBcdTime(bcdTimeStr);
        //Judge whether the south latitude and west longitude are based on the value of the status bit
        if (NumberUtil.getBitValue(status, 2) == 1) {
            lat = -lat;
        }
        if (NumberUtil.getBitValue(status, 3) == 1) {
            lon = -lon;
        }
        //location package package
        SensorLogData sensorLogData = new SensorLogData();
        //positioning status (1:positioning;0:Not positioning)
        sensorLogData.setLocationType((status & 0x0002) > 0 ? 1 : 0);
        //When the longitude and latitude are 0, regardless of whether the positioning status given by the device is positioning or not,it will be reset to no positioning
        if(lat==0||lon==0){
            sensorLogData.setLocationType(0);
        }
        //acc status
        sensorLogData.setLat(lat);
        sensorLogData.setLon(lon);
        sensorLogData.setAltitude(altitude);
        sensorLogData.setSpeed(speed);
        sensorLogData.setDirection(direction);
        DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
        sensorLogData.setTime(formatter.format(gpsZonedDateTime));
        parse54SensorLog(sensorLogData,msgBody);
        return sensorLogData;
    }

    /**
     * Sensor Log node data structure
     * @param sensorLogData
     * @param msgBody
     */
    private static void parse54SensorLog(SensorLogData sensorLogData, ByteBuf msgBody){
        List<Map<String,Object>> sensorList=new ArrayList<>();
        while (msgBody.readableBytes()>7) {
            Map<String, Object> sensorMap=new HashMap<>();
            int mask = msgBody.readUnsignedShort();
            //gnss time
            byte[] timeArr = new byte[6];
            msgBody.readBytes(timeArr);
            String bcdTimeStr = ByteBufUtil.hexDump(timeArr);
            ZonedDateTime gpsZonedDateTime = CommonUtil.parseBcdTime(bcdTimeStr);
            DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
            sensorMap.put("dateTime",formatter.format(gpsZonedDateTime));
            if ((mask & 0x01) > 0) {
                int light = msgBody.readUnsignedShort();
                sensorMap.put(SensorConstant.LIGHT, light);
            }
            if ((mask & 0x02) > 0) {
                //temperature
                String temperature = String.format("%.1f",CommonUtil.parseTemperature(msgBody.readShort()));
                if(!temperature.equals("-1000.0")) {
                    sensorMap.put(SensorConstant.TEMPERATURE, temperature);
                }
                //humidity
                int humidity = msgBody.readUnsignedShort();
                sensorMap.put(SensorConstant.HUMIDITY, String.format("%.1f",humidity * 0.1));
            }
            if ((mask & 0x04) > 0) {
                String ext_temp = String.format("%.1f",CommonUtil.parseTemperature(msgBody.readShort()));
                if(!ext_temp.equals("-1000.0")) {
                    sensorMap.put(AuxiliaryConstant.EXT_TEMP, ext_temp);
                }
            }
            if ((mask & 0x08) > 0) {
                //Accelerometer
                int x = msgBody.readUnsignedShort();
                int y = msgBody.readUnsignedShort();
                int z = msgBody.readUnsignedShort();
                x = x > 32768 ? (x - 65536) : x;
                y = y > 32768 ? (y - 65536) : y;
                z = z > 32768 ? (z - 65536) : z;
                sensorMap.put(SensorConstant.ACCELEROMETER, String.format("x:%s,y:%s,z:%s", x, y, z));
            }
            if ((mask & 0x10) > 0) {
                //Pressure
                Integer pressure = msgBody.readUnsignedShort()*100;
                sensorMap.put(SensorConstant.PRESSURE,pressure);
            }
            sensorList.add(sensorMap);
        }
        sensorLogData.setSensorList(sensorList);
    }
}
